/** @file
  FrameBufferBltLib - Library to perform blt operations on a frame buffer.

  Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "PiDxe.h"
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/BltLib.h>
#include <Library/DebugLib.h>

#if 0
#define VDEBUG DEBUG
#else
#define VDEBUG(x)
#endif

#define MAX_LINE_BUFFER_SIZE (SIZE_4KB * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))

UINTN                           mBltLibColorDepth;
UINTN                           mBltLibWidthInBytes;
UINTN                           mBltLibBytesPerPixel;
UINTN                           mBltLibWidthInPixels;
UINTN                           mBltLibHeight;
UINT8                           mBltLibLineBuffer[MAX_LINE_BUFFER_SIZE];
UINT8                           *mBltLibFrameBuffer;
EFI_GRAPHICS_PIXEL_FORMAT       mPixelFormat;
EFI_PIXEL_BITMASK               mPixelBitMasks;
INTN                            mPixelShl[4]; // R-G-B-Rsvd
INTN                            mPixelShr[4]; // R-G-B-Rsvd


VOID
ConfigurePixelBitMaskFormat (
  IN EFI_PIXEL_BITMASK          *BitMask
  )
{
  UINTN   Loop;
  UINT32  *Masks;
  UINT32  MergedMasks;

  MergedMasks = 0;
  Masks = (UINT32*) BitMask;
  for (Loop = 0; Loop < 3; Loop++) {
    ASSERT ((Loop == 3) || (Masks[Loop] != 0));
    ASSERT ((MergedMasks & Masks[Loop]) == 0);
    mPixelShl[Loop] = HighBitSet32 (Masks[Loop]) - 23 + (Loop * 8);
    if (mPixelShl[Loop] < 0) {
      mPixelShr[Loop] = -mPixelShl[Loop];
      mPixelShl[Loop] = 0;
    } else {
      mPixelShr[Loop] = 0;
    }
    MergedMasks = (UINT32) (MergedMasks | Masks[Loop]);
    DEBUG ((EFI_D_INFO, "%d: shl:%d shr:%d mask:%x\n", Loop, mPixelShl[Loop], mPixelShr[Loop], Masks[Loop]));
  }
  MergedMasks = (UINT32) (MergedMasks | Masks[3]);

  ASSERT (MergedMasks != 0);
  mBltLibBytesPerPixel = (UINTN) ((HighBitSet32 (MergedMasks) + 7) / 8);

  DEBUG ((EFI_D_INFO, "Bytes per pixel: %d\n", mBltLibBytesPerPixel));

  CopyMem (&mPixelBitMasks, BitMask, sizeof (*BitMask));
}


/**
  Configure the FrameBufferLib instance

  @param[in] FrameBuffer      Pointer to the start of the frame buffer
  @param[in] FrameBufferInfo  Describes the frame buffer characteristics

  @retval  EFI_INVALID_PARAMETER - Invalid parameter
  @retval  EFI_UNSUPPORTED - The BltLib does not support this configuration
  @retval  EFI_SUCCESS - Blt operation success

**/
EFI_STATUS
EFIAPI
BltLibConfigure (
  IN  VOID                                 *FrameBuffer,
  IN  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *FrameBufferInfo
  )
{
  STATIC EFI_PIXEL_BITMASK  RgbPixelMasks =
    { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 };
  STATIC EFI_PIXEL_BITMASK  BgrPixelMasks =
    { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 };

  switch (FrameBufferInfo->PixelFormat) {
  case PixelRedGreenBlueReserved8BitPerColor:
    ConfigurePixelBitMaskFormat (&RgbPixelMasks);
    break;
  case PixelBlueGreenRedReserved8BitPerColor:
    ConfigurePixelBitMaskFormat (&BgrPixelMasks);
    break;
  case PixelBitMask:
    ConfigurePixelBitMaskFormat (&(FrameBufferInfo->PixelInformation));
    break;
  case PixelBltOnly:
    ASSERT (FrameBufferInfo->PixelFormat != PixelBltOnly);
    return EFI_UNSUPPORTED;
  default:
    ASSERT (FALSE);
    return EFI_INVALID_PARAMETER;
  }
  mPixelFormat = FrameBufferInfo->PixelFormat;

  mBltLibFrameBuffer = (UINT8*) FrameBuffer;
  mBltLibWidthInPixels = (UINTN) FrameBufferInfo->HorizontalResolution;
  mBltLibHeight = (UINTN) FrameBufferInfo->VerticalResolution;
  mBltLibWidthInBytes = mBltLibWidthInPixels * mBltLibBytesPerPixel;

  ASSERT (mBltLibWidthInBytes < sizeof (mBltLibLineBuffer));

  return EFI_SUCCESS;
}


/**
  Performs a UEFI Graphics Output Protocol Blt operation.

  @param[in,out] BltBuffer     - The data to transfer to screen
  @param[in]     BltOperation  - The operation to perform
  @param[in]     SourceX       - The X coordinate of the source for BltOperation
  @param[in]     SourceY       - The Y coordinate of the source for BltOperation
  @param[in]     DestinationX  - The X coordinate of the destination for BltOperation
  @param[in]     DestinationY  - The Y coordinate of the destination for BltOperation
  @param[in]     Width         - The width of a rectangle in the blt rectangle in pixels
  @param[in]     Height        - The height of a rectangle in the blt rectangle in pixels
  @param[in]     Delta         - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
                                 If a Delta of 0 is used, the entire BltBuffer will be operated on.
                                 If a subrectangle of the BltBuffer is used, then Delta represents
                                 the number of bytes in a row of the BltBuffer.

  @retval  EFI_DEVICE_ERROR - A hardware error occured
  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
  @retval  EFI_SUCCESS - Blt operation success

**/
EFI_STATUS
EFIAPI
BltLibGopBlt (
  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer, OPTIONAL
  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION     BltOperation,
  IN  UINTN                                 SourceX,
  IN  UINTN                                 SourceY,
  IN  UINTN                                 DestinationX,
  IN  UINTN                                 DestinationY,
  IN  UINTN                                 Width,
  IN  UINTN                                 Height,
  IN  UINTN                                 Delta
  )
{
  switch (BltOperation) {
  case EfiBltVideoToBltBuffer:
    return BltLibVideoToBltBufferEx (
             BltBuffer,
             SourceX,
             SourceY,
             DestinationX,
             DestinationY,
             Width,
             Height,
             Delta
             );

  case EfiBltVideoToVideo:
    return BltLibVideoToVideo (
             SourceX,
             SourceY,
             DestinationX,
             DestinationY,
             Width,
             Height
             );

  case EfiBltVideoFill:
    return BltLibVideoFill (
             BltBuffer,
             DestinationX,
             DestinationY,
             Width,
             Height
             );

  case EfiBltBufferToVideo:
    return BltLibBufferToVideoEx (
             BltBuffer,
             SourceX,
             SourceY,
             DestinationX,
             DestinationY,
             Width,
             Height,
             Delta
             );
  default:
    return EFI_INVALID_PARAMETER;
  }
}


/**
  Performs a UEFI Graphics Output Protocol Blt Video Fill.

  @param[in]  Color         Color to fill the region with
  @param[in]  DestinationX  X location to start fill operation
  @param[in]  DestinationY  Y location to start fill operation
  @param[in]  Width         Width (in pixels) to fill
  @param[in]  Height        Height to fill

  @retval  EFI_DEVICE_ERROR - A hardware error occured
  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
  @retval  EFI_SUCCESS - The sizes were returned

**/
EFI_STATUS
EFIAPI
BltLibVideoFill (
  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *Color,
  IN  UINTN                                 DestinationX,
  IN  UINTN                                 DestinationY,
  IN  UINTN                                 Width,
  IN  UINTN                                 Height
  )
{
  UINTN                           DstY;
  VOID                            *BltMemDst;
  UINTN                           X;
  UINT8                           Uint8;
  UINT32                          Uint32;
  UINT64                          WideFill;
  BOOLEAN                         UseWideFill;
  BOOLEAN                         LineBufferReady;
  UINTN                           Offset;
  UINTN                           WidthInBytes;
  UINTN                           SizeInBytes;

  //
  // BltBuffer to Video: Source is BltBuffer, destination is Video
  //
  if (DestinationY + Height > mBltLibHeight) {
    DEBUG ((EFI_D_INFO, "VideoFill: Past screen (Y)\n"));
    return EFI_INVALID_PARAMETER;
  }

  if (DestinationX + Width > mBltLibWidthInPixels) {
    DEBUG ((EFI_D_INFO, "VideoFill: Past screen (X)\n"));
    return EFI_INVALID_PARAMETER;
  }

  if (Width == 0 || Height == 0) {
    DEBUG ((EFI_D_INFO, "VideoFill: Width or Height is 0\n"));
    return EFI_INVALID_PARAMETER;
  }

  WidthInBytes = Width * mBltLibBytesPerPixel;

  Uint32 = *(UINT32*) Color;
  WideFill =
    (UINT32) (
        (((Uint32 << mPixelShl[0]) >> mPixelShr[0]) & mPixelBitMasks.RedMask) |
        (((Uint32 << mPixelShl[1]) >> mPixelShr[1]) & mPixelBitMasks.GreenMask) |
        (((Uint32 << mPixelShl[2]) >> mPixelShr[2]) & mPixelBitMasks.BlueMask)
      );
  VDEBUG ((EFI_D_INFO, "VideoFill: color=0x%x, wide-fill=0x%x\n", Uint32, WideFill));

  //
  // If the size of the pixel data evenly divides the sizeof
  // WideFill, then a wide fill operation can be used
  //
  UseWideFill = TRUE;
  if ((sizeof (WideFill) % mBltLibBytesPerPixel) == 0) {
    for (X = mBltLibBytesPerPixel; X < sizeof (WideFill); X++) {
      ((UINT8*)&WideFill)[X] = ((UINT8*)&WideFill)[X % mBltLibBytesPerPixel];
    }
  } else {
    //
    // If all the bytes in the pixel are the same value, then use
    // a wide fill operation.
    //
    for (
      X = 1, Uint8 = ((UINT8*)&WideFill)[0];
      X < mBltLibBytesPerPixel;
      X++) {
      if (Uint8 != ((UINT8*)&WideFill)[X]) {
        UseWideFill = FALSE;
        break;
      }
    }
    if (UseWideFill) {
      SetMem ((VOID*) &WideFill, sizeof (WideFill), Uint8);
    }
  }

  if (UseWideFill && (DestinationX == 0) && (Width == mBltLibWidthInPixels)) {
    VDEBUG ((EFI_D_INFO, "VideoFill (wide, one-shot)\n"));
    Offset = DestinationY * mBltLibWidthInPixels;
    Offset = mBltLibBytesPerPixel * Offset;
    BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset);
    SizeInBytes = WidthInBytes * Height;
    if (SizeInBytes >= 8) {
      SetMem32 (BltMemDst, SizeInBytes & ~3, (UINT32) WideFill);
      SizeInBytes = SizeInBytes & 3;
    }
    if (SizeInBytes > 0) {
      SetMem (BltMemDst, SizeInBytes, (UINT8)(UINTN) WideFill);
    }
  } else {
    LineBufferReady = FALSE;
    for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) {
      Offset = (DstY * mBltLibWidthInPixels) + DestinationX;
      Offset = mBltLibBytesPerPixel * Offset;
      BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset);

      if (UseWideFill && (((UINTN) BltMemDst & 7) == 0)) {
        VDEBUG ((EFI_D_INFO, "VideoFill (wide)\n"));
        SizeInBytes = WidthInBytes;
        if (SizeInBytes >= 8) {
          SetMem64 (BltMemDst, SizeInBytes & ~7, WideFill);
          SizeInBytes = SizeInBytes & 7;
        }
        if (SizeInBytes > 0) {
          CopyMem (BltMemDst, (VOID*) &WideFill, SizeInBytes);
        }
      } else {
        VDEBUG ((EFI_D_INFO, "VideoFill (not wide)\n"));
        if (!LineBufferReady) {
          CopyMem (mBltLibLineBuffer, &WideFill, mBltLibBytesPerPixel);
          for (X = 1; X < Width; ) {
            CopyMem(
              (mBltLibLineBuffer + (X * mBltLibBytesPerPixel)),
              mBltLibLineBuffer,
              MIN (X, Width - X) * mBltLibBytesPerPixel
              );
            X = X + MIN (X, Width - X);
          }
          LineBufferReady = TRUE;
        }
        CopyMem (BltMemDst, mBltLibLineBuffer, WidthInBytes);
      }
    }
  }

  return EFI_SUCCESS;
}


/**
  Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation.

  @param[out] BltBuffer     Output buffer for pixel color data
  @param[in]  SourceX       X location within video
  @param[in]  SourceY       Y location within video
  @param[in]  Width         Width (in pixels)
  @param[in]  Height        Height

  @retval  EFI_DEVICE_ERROR - A hardware error occured
  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
  @retval  EFI_SUCCESS - The sizes were returned

**/
EFI_STATUS
EFIAPI
BltLibVideoToBltBuffer (
  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
  IN  UINTN                                 SourceX,
  IN  UINTN                                 SourceY,
  IN  UINTN                                 Width,
  IN  UINTN                                 Height
  )
{
  return BltLibVideoToBltBufferEx (
           BltBuffer,
           SourceX,
           SourceY,
           0,
           0,
           Width,
           Height,
           0
           );
}


/**
  Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation
  with extended parameters.

  @param[out] BltBuffer     Output buffer for pixel color data
  @param[in]  SourceX       X location within video
  @param[in]  SourceY       Y location within video
  @param[in]  DestinationX  X location within BltBuffer
  @param[in]  DestinationY  Y location within BltBuffer
  @param[in]  Width         Width (in pixels)
  @param[in]  Height        Height
  @param[in]  Delta         Number of bytes in a row of BltBuffer

  @retval  EFI_DEVICE_ERROR - A hardware error occured
  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
  @retval  EFI_SUCCESS - The sizes were returned

**/
EFI_STATUS
EFIAPI
BltLibVideoToBltBufferEx (
  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
  IN  UINTN                                 SourceX,
  IN  UINTN                                 SourceY,
  IN  UINTN                                 DestinationX,
  IN  UINTN                                 DestinationY,
  IN  UINTN                                 Width,
  IN  UINTN                                 Height,
  IN  UINTN                                 Delta
  )
{
  UINTN                           DstY;
  UINTN                           SrcY;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL   *Blt;
  VOID                            *BltMemSrc;
  VOID                            *BltMemDst;
  UINTN                           X;
  UINT32                          Uint32;
  UINTN                           Offset;
  UINTN                           WidthInBytes;

  //
  // Video to BltBuffer: Source is Video, destination is BltBuffer
  //
  if (SourceY + Height > mBltLibHeight) {
    return EFI_INVALID_PARAMETER;
  }

  if (SourceX + Width > mBltLibWidthInPixels) {
    return EFI_INVALID_PARAMETER;
  }

  if (Width == 0 || Height == 0) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // If Delta is zero, then the entire BltBuffer is being used, so Delta
  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
  // the number of bytes in each row can be computed.
  //
  if (Delta == 0) {
    Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
  }

  WidthInBytes = Width * mBltLibBytesPerPixel;

  //
  // Video to BltBuffer: Source is Video, destination is BltBuffer
  //
  for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) {

    Offset = (SrcY * mBltLibWidthInPixels) + SourceX;
    Offset = mBltLibBytesPerPixel * Offset;
    BltMemSrc = (VOID *) (mBltLibFrameBuffer + Offset);

    if (mPixelFormat == PixelBlueGreenRedReserved8BitPerColor) {
      BltMemDst =
        (VOID *) (
            (UINT8 *) BltBuffer +
            (DstY * Delta) +
            (DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))
          );
    } else {
      BltMemDst = (VOID *) mBltLibLineBuffer;
    }

    CopyMem (BltMemDst, BltMemSrc, WidthInBytes);

    if (mPixelFormat != PixelBlueGreenRedReserved8BitPerColor) {
      for (X = 0; X < Width; X++) {
        Blt         = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + (DestinationX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
        Uint32 = *(UINT32*) (mBltLibLineBuffer + (X * mBltLibBytesPerPixel));
        *(UINT32*) Blt =
          (UINT32) (
              (((Uint32 & mPixelBitMasks.RedMask)   >> mPixelShl[0]) << mPixelShr[0]) |
              (((Uint32 & mPixelBitMasks.GreenMask) >> mPixelShl[1]) << mPixelShr[1]) |
              (((Uint32 & mPixelBitMasks.BlueMask)  >> mPixelShl[2]) << mPixelShr[2])
            );
      }
    }
  }

  return EFI_SUCCESS;
}


/**
  Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation.

  @param[in]  BltBuffer     Output buffer for pixel color data
  @param[in]  DestinationX  X location within video
  @param[in]  DestinationY  Y location within video
  @param[in]  Width         Width (in pixels)
  @param[in]  Height        Height

  @retval  EFI_DEVICE_ERROR - A hardware error occured
  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
  @retval  EFI_SUCCESS - The sizes were returned

**/
EFI_STATUS
EFIAPI
BltLibBufferToVideo (
  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
  IN  UINTN                                 DestinationX,
  IN  UINTN                                 DestinationY,
  IN  UINTN                                 Width,
  IN  UINTN                                 Height
  )
{
  return BltLibBufferToVideoEx (
           BltBuffer,
           0,
           0,
           DestinationX,
           DestinationY,
           Width,
           Height,
           0
           );
}


/**
  Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation
  with extended parameters.

  @param[in]  BltBuffer     Output buffer for pixel color data
  @param[in]  SourceX       X location within BltBuffer
  @param[in]  SourceY       Y location within BltBuffer
  @param[in]  DestinationX  X location within video
  @param[in]  DestinationY  Y location within video
  @param[in]  Width         Width (in pixels)
  @param[in]  Height        Height
  @param[in]  Delta         Number of bytes in a row of BltBuffer

  @retval  EFI_DEVICE_ERROR - A hardware error occured
  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
  @retval  EFI_SUCCESS - The sizes were returned

**/
EFI_STATUS
EFIAPI
BltLibBufferToVideoEx (
  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
  IN  UINTN                                 SourceX,
  IN  UINTN                                 SourceY,
  IN  UINTN                                 DestinationX,
  IN  UINTN                                 DestinationY,
  IN  UINTN                                 Width,
  IN  UINTN                                 Height,
  IN  UINTN                                 Delta
  )
{
  UINTN                           DstY;
  UINTN                           SrcY;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL   *Blt;
  VOID                            *BltMemSrc;
  VOID                            *BltMemDst;
  UINTN                           X;
  UINT32                          Uint32;
  UINTN                           Offset;
  UINTN                           WidthInBytes;

  //
  // BltBuffer to Video: Source is BltBuffer, destination is Video
  //
  if (DestinationY + Height > mBltLibHeight) {
    return EFI_INVALID_PARAMETER;
  }

  if (DestinationX + Width > mBltLibWidthInPixels) {
    return EFI_INVALID_PARAMETER;
  }

  if (Width == 0 || Height == 0) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // If Delta is zero, then the entire BltBuffer is being used, so Delta
  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
  // the number of bytes in each row can be computed.
  //
  if (Delta == 0) {
    Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
  }

  WidthInBytes = Width * mBltLibBytesPerPixel;

  for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {

    Offset = (DstY * mBltLibWidthInPixels) + DestinationX;
    Offset = mBltLibBytesPerPixel * Offset;
    BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset);

    if (mPixelFormat == PixelBlueGreenRedReserved8BitPerColor) {
      BltMemSrc = (VOID *) ((UINT8 *) BltBuffer + (SrcY * Delta));
    } else {
      for (X = 0; X < Width; X++) {
        Blt =
          (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) (
              (UINT8 *) BltBuffer +
              (SrcY * Delta) +
              ((SourceX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))
            );
        Uint32 = *(UINT32*) Blt;
        *(UINT32*) (mBltLibLineBuffer + (X * mBltLibBytesPerPixel)) =
          (UINT32) (
              (((Uint32 << mPixelShl[0]) >> mPixelShr[0]) & mPixelBitMasks.RedMask) |
              (((Uint32 << mPixelShl[1]) >> mPixelShr[1]) & mPixelBitMasks.GreenMask) |
              (((Uint32 << mPixelShl[2]) >> mPixelShr[2]) & mPixelBitMasks.BlueMask)
            );
      }
      BltMemSrc = (VOID *) mBltLibLineBuffer;
    }

    CopyMem (BltMemDst, BltMemSrc, WidthInBytes);
  }

  return EFI_SUCCESS;
}


/**
  Performs a UEFI Graphics Output Protocol Blt Video to Video operation

  @param[in]  SourceX       X location within video
  @param[in]  SourceY       Y location within video
  @param[in]  DestinationX  X location within video
  @param[in]  DestinationY  Y location within video
  @param[in]  Width         Width (in pixels)
  @param[in]  Height        Height

  @retval  EFI_DEVICE_ERROR - A hardware error occured
  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
  @retval  EFI_SUCCESS - The sizes were returned

**/
EFI_STATUS
EFIAPI
BltLibVideoToVideo (
  IN  UINTN                                 SourceX,
  IN  UINTN                                 SourceY,
  IN  UINTN                                 DestinationX,
  IN  UINTN                                 DestinationY,
  IN  UINTN                                 Width,
  IN  UINTN                                 Height
  )
{
  VOID                            *BltMemSrc;
  VOID                            *BltMemDst;
  UINTN                           Offset;
  UINTN                           WidthInBytes;
  INTN                            LineStride;

  //
  // Video to Video: Source is Video, destination is Video
  //
  if (SourceY + Height > mBltLibHeight) {
    return EFI_INVALID_PARAMETER;
  }

  if (SourceX + Width > mBltLibWidthInPixels) {
    return EFI_INVALID_PARAMETER;
  }

  if (DestinationY + Height > mBltLibHeight) {
    return EFI_INVALID_PARAMETER;
  }

  if (DestinationX + Width > mBltLibWidthInPixels) {
    return EFI_INVALID_PARAMETER;
  }

  if (Width == 0 || Height == 0) {
    return EFI_INVALID_PARAMETER;
  }

  WidthInBytes = Width * mBltLibBytesPerPixel;

  Offset = (SourceY * mBltLibWidthInPixels) + SourceX;
  Offset = mBltLibBytesPerPixel * Offset;
  BltMemSrc = (VOID *) (mBltLibFrameBuffer + Offset);

  Offset = (DestinationY * mBltLibWidthInPixels) + DestinationX;
  Offset = mBltLibBytesPerPixel * Offset;
  BltMemDst = (VOID *) (mBltLibFrameBuffer + Offset);

  LineStride = mBltLibWidthInBytes;
  if ((UINTN) BltMemDst > (UINTN) BltMemSrc) {
    LineStride = -LineStride;
  }

  while (Height > 0) {
    CopyMem (BltMemDst, BltMemSrc, WidthInBytes);

    BltMemSrc = (VOID*) ((UINT8*) BltMemSrc + LineStride);
    BltMemDst = (VOID*) ((UINT8*) BltMemDst + LineStride);
    Height--;
  }

  return EFI_SUCCESS;
}


/**
  Returns the sizes related to the video device

  @param[out]  Width   Width (in pixels)
  @param[out]  Height  Height (in pixels)

  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
  @retval  EFI_SUCCESS - The sizes were returned

**/
EFI_STATUS
EFIAPI
BltLibGetSizes (
  OUT UINTN                                 *Width,  OPTIONAL
  OUT UINTN                                 *Height  OPTIONAL
  )
{
  if (Width != NULL) {
    *Width = mBltLibWidthInPixels;
  }
  if (Height != NULL) {
    *Height = mBltLibHeight;
  }

  return EFI_SUCCESS;
}

